/*
 * tcs230.cpp
 *
 * Created: 22.03.2014 15:16:44
 *  Author: aamonster
 */ 


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/pgmspace.h>

// tcs230 connection:
// OE - PD4
// S1 - 
// S0 - 
// S2 - PD2
// S3 - PD3
// Vcc - PD0
// Out - PD1
// LEDs: G - PC5, R - PC4, B - PC3
enum PinsInPortC {
	GREEN = 1 << PORTC5,
	RED = 1 << PORTC4,
	BLUE = 1 << PORTC3,
	};
enum PinsInPortD {
	VCC = 1 << PORTD0,
	OUT = 1 << PORTD1,
	S2 = 1 << PORTD2,
	S3 = 1 << PORTD3,
	OE = 1 << PORTD4,
	S0 = 0, // NC
	S1 = 0, // NC
};


enum ScaleFactor {
	sfPowerDown=0,
	sf02 = S1,
	sf20 = S0,
	sfFull = S1|S0,
	sfMask = S1|S0,
	};
	
enum ColorChannel {
	ccRed = 0,
	ccBlue = S3,
	ccClear = S2,
	ccGreen = S3|S2,
	ccMask = S3|S2,
	};
	
const uint8_t tcs230_out_mask = sfMask|ccMask|VCC|OE;
const uint8_t ledsMask = RED|BLUE|GREEN;

uint16_t measure(uint8_t mode) {
	PORTD |= VCC; // power on
	PORTD = (PORTD & ~tcs230_out_mask) | mode | VCC; // OE = 0 - turn on
	_delay_us(10);
	uint16_t result = 0;
	while (PIND&OUT) ; // wait for 0
	while (!(PIND&OUT)) {
		result++;
		if(result==16383) break;
	}
	PORTD |= OE;
	PORTD &= ~VCC;
	return result;
}

int main(void)
{
	DDRC = ledsMask;
	DDRD = tcs230_out_mask;
	
	PORTC = 0;

	// test leds
	for (int i=3; i; i--) {
		PORTC = RED;
		_delay_ms(300);
		PORTC = GREEN;
		_delay_ms(300);
		PORTC = BLUE;
		_delay_ms(300);
	}

	float r_max=0,g_max=0,b_max=0,w_max=0;
	for(int i=0; i<64; i++) {
		float r,g,b,w;
		r = measure(ccRed|sfFull);
		g = measure(ccGreen|sfFull);
		b = measure(ccBlue|sfFull);
		w = measure(ccClear|sfFull);
		r_max += r; g_max += g; b_max += b; w_max += w;
		_delay_ms(10);
	}
	r_max /= 64; g_max /= 64; b_max /= 64; w_max /= 64;

	
	while(1) {
		PORTD &= ~ledsMask;
		float r,g,b,w;
		r = measure(ccRed|sfFull);
		g = measure(ccGreen|sfFull);
		b = measure(ccBlue|sfFull);
		w = measure(ccClear|sfFull);
		r = r_max/(r+.1);
		g = g_max/(g+.1);
		b = b_max/(b+.1);
		w = w_max/(w+.1);
		//r /= (w+0.01); g /= (w+0.01); b /= (w+0.01);
		//r = (w+1)/(r+1);
		//g = (w+1)/(g+1);
		//b = (w+1)/(b+1);
		r*=r; g*=g; b*=b; w*=w;
		float s = r>g ? r : g;
		s = s>b ? s : b;
		r/=s; g/=s; b/=s;
		//r*=16; g*=16; b*=16;
		//int h = (int)(r*8);
		//if(h>7) h=7;
		//PORTD &= ~ledsMask;
		//PORTD |= h << 1;
		//_delay_ms(500);
		r*=32; g*=32; b*=32; w*=32;
		
		for (uint16_t k=0; k<8; k++) {
			for (uint16_t i=0; i<64; i++) {
				PORTC &= ~ledsMask;
				if(i<r) PORTC |= RED;
				if(i<g) PORTC |= GREEN;
				if(i<b) PORTC |= BLUE;
				//if(i<w) PORTD |= WHITE;
				_delay_us(100);
				//PORTD &= ~ledsMask;
				//_delay_ms(290);
			}
		}
		PORTD &= ~ledsMask;
	}
	
    while(1)
    {
        PORTD &= ~ledsMask;
		_delay_ms(200);
		PORTD |= RED; // red
		_delay_ms(200);
		PORTD |= GREEN; // green
		_delay_ms(200);
		PORTD |= BLUE; // blue
		_delay_ms(200);
		PORTC ^= OE; // switch power
    }
}